
package cn.zlg.common.datasafety;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.Table;

import cn.zlg.common.datasafety.DataCheckConfig.CheckDetail;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.util.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DefaultDataDeleteChecker<ID> implements DataDeleteChecker<ID> {

    private static final Logger logger = LoggerFactory.getLogger(DefaultDataDeleteChecker.class);

    Map<String, DataCheckConfig> checkMap = new HashMap<String, DataCheckConfig>();
    Map<String, DataCheckConfig> methodMap = new HashMap<String, DataCheckConfig>();
    private EntityManager entityManager;

    @Override
    public void check(Class<?> entityClass, ID id) throws CannotDeleteDataException {
        String table = extractTableName(entityClass);
        check(table, id);
    }

    private String extractTableName(Class<?> entityClass) {
        Table annotation = entityClass.getAnnotation(Table.class);
        if (annotation != null) {
            return annotation.name();
        }
        org.hibernate.annotations.Table annotation2 = entityClass.getAnnotation(org.hibernate.annotations.Table.class);
        if (annotation2 != null) {
            return annotation2.appliesTo();
        }
        logger.error("找不到" + entityClass.getName() + "对应的表名，将忽略");
        return null;
    }

    @Override
    public void check(String tableName, ID id) throws CannotDeleteDataException {
        if (tableName == null) {
            return;
        }
        DataCheckConfig check = checkMap.get(tableName);
        StringBuilder sb = new StringBuilder();
        if (check == null) {
            check = methodMap.get(tableName);
        }
        if (check != null && check.getChecks() != null && check.getChecks().size() > 0) {
            for (CheckDetail cd : check.getChecks()) {
                if ("default".equals(cd.getType()) || cd.getType() == null) {
                    String msg = checkTableAndColumn(cd, id);
                    if (msg != null) {
                        sb.append(msg).append(" , ");
                    }
                } else if ("sql".equals(cd.getType())) {
                    String msg = checkBySql(cd, id);
                    if (msg != null) {
                        sb.append(msg).append(" , ");
                    }
                } else {
                    logger.warn("【数据安全检查】不识别的类型:" + tableName + "    " + cd.getType());
                }
            }
            if (sb.length() > 0) {
                throw new CannotDeleteDataException("（删除提示）该项目目前无法删除，请先取消相关联的项目“" + sb.toString() + "”后再删除。");
            }
        }
    }

    private String checkBySql(CheckDetail cd, ID id) {
        Query query = entityManager.createNativeQuery(cd.getSql());
        if (query.getParameters().size() > 0) {
            query.setParameter("id", id);
        }
        List<Object[]> list = query.getResultList();
        if (list.isEmpty()) {
            return null;
        }
        if (list.size() == 1) {
            Object[] columns = list.get(0);
            if (columns.length == 1 && (columns[0] instanceof Long || columns[0] instanceof BigInteger)) {
                int count = 0;
                if (columns[0] instanceof Long) {
                    count = ((Long) columns[0]).intValue();
                } else {
                    count = ((BigInteger) columns[0]).intValue();
                }
                if (count > 0) {
                    return "关联数据:" + cd.getComment() + "(" + count + "条)";
                    // throw new CannotDeleteDataException(
                    // "存在关联数据:" + cd.getComment() + "，关联数据条数为" + count + ",删除前请先删除关联数据");
                } else {
                    return null;
                }
            } else {
                return gatherMessage(cd, list);
            }
        } else {
            return gatherMessage(cd, list);
        }
    }

    private String gatherMessage(CheckDetail cd, List list) {
        StringBuilder sb = new StringBuilder();
        // sb.append(cd.getComment()).append(":关联").append(list.size()).append("条\n");
        for (Object arr : list) {
            if (arr instanceof Object[]) {
                Object[] objects = (Object[]) arr;
                for (int i = 0; i < objects.length; i++) {
                    sb.append(cd.getComment()).append("(");
                    sb.append(objects[i]);
                    if (i != objects.length - 1) {
                        sb.append(",");
                    }
                    sb.append(") , ");
                }
            } else {
                sb.append(cd.getComment()).append("(").append(arr).append(")");
            }

        }
        return sb.toString();
        // throw new CannotDeleteDataException(sb.toString());
    }

    private String checkTableAndColumn(CheckDetail cd, ID id) {
        if (StringUtils.isNotBlank(cd.getTipColumns())) {
            String sql = "select " + cd.getTipColumns() + " from " + cd.getRelatedTable() + " where "
                    + cd.getRelatedColumn() + " = " + id;
            if (cd.isLogicalDelete()) {
                sql += " and DELETE_FLAG='0'";
            }
            Query query = entityManager.createNativeQuery(sql);
            List<Object[]> list = query.getResultList();
            if (list.isEmpty()) {
                return null;
            }
            return gatherMessage(cd, list);
        } else {
            String sql = "select count(*) from " + cd.getRelatedTable() + " where " + cd.getRelatedColumn() + " = "
                    + id;
            if (cd.isLogicalDelete()) {
                sql += " and DELETE_FLAG='0'";
            }
            Query query = entityManager.createNativeQuery(sql);
            java.math.BigInteger count = (java.math.BigInteger) query.getSingleResult();
            if (count.intValue() > 0) {
                // throw new CannotDeleteDataException("存在关联数据:" + cd.getComment() + "，关联数据条数为" + count +
                // ",删除前请先删除关联数据");
                return "存在关联数据:" + cd.getComment() + "，关联数据条数为" + count + ",删除前请先删除关联数据";
            }
            return null;
        }

    }

    public EntityManager getEntityManager() {
        return entityManager;
    }

    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public void setCheckConfig(List<DataCheckConfig> list) {
        Assert.notNull(list, "list不能为null");
        for (DataCheckConfig d : list) {
            checkMap.put(d.getTable().toUpperCase(), d);
            if (d.getMethod() != null) {
                methodMap.put(d.getMethod(), d);
            }
        }
    }
}
